home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-01 / kvbugs.zip / TVBUG-KV.TXT < prev   
Text File  |  1992-10-29  |  22KB  |  735 lines

  1.                TurboVision Bug Listing     10/29/92
  2.  
  3. The following is an *informal* bug list for Borland C++ / TurboVision.
  4. The list is maintained strictly as a service to other forum members;
  5. I do not receive any compensation for this.  All information is
  6. supplied on an "as-is" basis -- I make no guarantees that the problems
  7. or solutions are accurate.
  8.  
  9. Wherever possible, I have checked whether or not the bugs still exist
  10. in Borland C++ 3.1 (I never remember the A/F version numbers).
  11.  
  12. When I refer a bug being demonstrated or
  13. confirmed by someone, I mean simply that I read their forum message to
  14. that effect.  I apologize in advance if I have misread or misquoted
  15. anyone.
  16.  
  17. The list contains bugs that I have found while perusing the forum,
  18. and is somewhat slanted to TurboVision and C++ (I don't use OWL or
  19. Templates yet, so I haven't been paying attention to them as much). If
  20. you send me bugs in these areas, I will add them to the list.
  21.  
  22. If you have any corrections or additions to this list, please send
  23. them to me and I will add them to the list.  Hope this list helps.
  24.  
  25. -- Ken Vogel 74007,564 --
  26.  
  27.  
  28. ------------------------------------------------------------------
  29. Id:        KV-1
  30. Location:  TurboVision
  31.  
  32. Problem:  Unfreed memory when TStreamable descendent object pointers
  33.           are written to an ofpstream.
  34.  
  35. Version:  Exists in BC 3.0
  36.           Appears to be fixed in 3.1 (although I have not tested memory
  37.           counters to verify that there is no leak, as Borland made other
  38.           changes to this file as well)
  39.  
  40. Proposed Solution:  The objs member of ofpstream contains pointers to
  41. TPWObj elements, which are created while the stream is written.  However,
  42. when the stream is deleted, the TV code sets the shouldDelete member of
  43. objs to False, preventing the TPWObj members from being properly deleted.
  44.  
  45. **** TOBJSTRM.CPP Line 99
  46.  
  47. ==== OLD ====
  48. TStreamableTypes::~TStreamableTypes()
  49. {
  50.     shouldDelete = False;
  51. }
  52.  
  53. ===== NEW ====
  54. TStreamableTypes::~TStreamableTypes()
  55. {
  56. }
  57.  
  58. **** TOBJSTRM.CPP Line 512
  59.  
  60. ==== OLD ====
  61. opstream::~opstream()
  62. {
  63.     objs->shouldDelete = False;
  64.     objs->shutDown();
  65.     delete objs;
  66. }
  67. ===== NEW ====
  68. opstream::~opstream()
  69. {
  70.     objs->destroy (objs);
  71. }
  72. *** END ***
  73.  
  74. ------------------------------------------------------------------
  75. Id:        KV-2
  76.  
  77. Location:  TurboVision
  78.  
  79. Problem:      TFileDialog does not correctly read subdirectories.
  80.  
  81. Version:  Exists in BC 3.0, fixed in 3.1
  82.  
  83. Proposed Solution:  A call to fnmerge incorrectly uses *.* as the
  84. extension when building the directory mask.  In BC 3.0, the fnmerge
  85. no longer accepts this.
  86.  
  87. **** TFILLIST.CPP Line 170
  88.  
  89. ==== OLD ====
  90.     fnmerge( path, drive, dir, file, "*.*" );
  91. ===== NEW ====
  92.     fnmerge( path, drive, dir, "*", ".*" );
  93.  
  94. ------------------------------------------------------------------
  95. Id:        KV-3
  96.  
  97. Location:  TurboVision
  98.  
  99. Problem:   TGroup::valid is not re-entrant.  If you have multiple windows
  100.            on your desktop, and wish to display a message during your
  101.            valid method in one of the windows, other windows will receive
  102.            an incorrect command parameter to their valid.
  103.  
  104. Version:  Exists in BC 3.0, and in 3.1
  105.  
  106.  
  107. Proposed Solution:
  108.          TGROUP uses a global static variable to pass the current
  109.          command to the iterator function isInvalid.  Pass this parameter
  110.          via a pointer instead.
  111.  
  112. **** TGROUP.CPP Line 503
  113.  
  114. ==== OLD ====
  115. static ushort cmd;
  116.  
  117. Boolean isInvalid( TView *p, void * )
  118. {
  119.     return Boolean( !p->valid( cmd ) );
  120. }
  121.  
  122. Boolean TGroup::valid( ushort command )
  123. {
  124.     cmd = command;
  125.     return Boolean( firstThat( isInvalid, 0 ) == 0 );
  126. }
  127.  
  128. ===== NEW ====
  129. Boolean isInvalid( TView *p, void *commandP )
  130. {
  131.     return Boolean( !p->valid( *(ushort *)commandP ) );
  132. }
  133.  
  134. Boolean TGroup::valid( ushort command )
  135. {
  136.     return Boolean( firstThat( isInvalid, &command ) == 0 );
  137. }
  138.  
  139. ------------------------------------------------------------------
  140. Id:        KV-4
  141.  
  142. Location:  TurboVision
  143.  
  144. Problem:   Writing duplicate pointers to descendants of TStreamable
  145.            streams may writes out a new copy of the pointed to objects
  146.            each time.  Occurs only if pointers to two or more different
  147.            classes are written to a stream.
  148.  
  149. Version:  Exists in BC 3.0, fixed in 3.1
  150.  
  151.  
  152. Proposed Solution:
  153.          The list of pointers which have been written to the stream is
  154.          maintained in a descendent of TNSSortedCollection by ofpstream.
  155.          This descendent incorrectly performs a pointer comparison when
  156.          searching to see if a pointer has already been written to the
  157.          stream.  In some conditions, this causes the search to terminate
  158.          before the pointer is found, thus causing the same object to
  159.          be written twice.
  160.  
  161. **** TOBJSTRM.CPP Line 123
  162.  
  163. ==== OLD ====
  164. int TPWrittenObjects::compare( void *o1, void *o2 )
  165. {
  166.     if( o1 == o2 )
  167.         return 0;
  168.     else if( o1 < o2 )
  169.         return -1;
  170.     else
  171.         return 1;
  172. }
  173.  
  174. ===== NEW ====
  175. int TPWrittenObjects::compare( void *o1, void *o2 )
  176. {
  177.     if( o1 == o2 )
  178.         return 0;
  179.     else if( (long)o1 < (long)o2 )
  180.         return -1;
  181.     else
  182.         return 1;
  183. }
  184.  
  185. ------------------------------------------------------------------
  186. Id:        KV-5
  187.  
  188. Location:  TurboVision
  189.  
  190. Problem:   Directories with names less than two characters in length
  191.            are not displayed in a TFileList (or TFileDialog)
  192.  
  193. Version:  Exists in BC 3.0, and in 3.1
  194.  
  195. Proposed Solution:  For some reason, TFileList elimates directories
  196.                     whose length is less than or equal to four.
  197.  
  198. **** TFILLIST.CPP Line 187
  199.  
  200. ==== OLD ====
  201.     if( strlen( dir ) > 4 )
  202.         {
  203.  
  204. ===== NEW ====
  205.     if( strlen( dir ) > 1 )
  206.         {
  207.  
  208. ------------------------------------------------------------------
  209. Id:        KV-6
  210.  
  211. Location:  Compiler optimization
  212.  
  213. Problem:  (Sorry, I don't have the name of the person who originated this
  214. message.  Thanks, whoever you are. --KV--)
  215.  
  216. Version:  Exists in BC 3.0. Seems to be fixed in 3.1
  217.  
  218.  
  219. This code demonstrates the problem (with loop optimizer ON)
  220.  
  221. int main()
  222. {
  223.   for( int i = 10; i>=0; i--)
  224.      cout << (10-i) << '\n';
  225.  
  226.    return 0;
  227. }
  228.  
  229. The program output should be "0,1,2,...,10".
  230. Instead, the output is "0,-1,-2,...,-10".
  231.  
  232. The bug is in the loop optimizer.  The compiler introduces an
  233. induction variable to replace the expression (10-i),
  234. which it stores in register DI. The compiler correctly
  235. initializes DI to 0, but in the iteration step it *decrements*
  236. DI when it should *increment* it.  Looks like you tripped over
  237.  the "double negative."
  238.  
  239. Proposed Solution:  Don't make this code construct, or turn off loop
  240.     optimization for those sections of the code which use constructs
  241.     similar to this one.
  242.  
  243.  
  244. ------------------------------------------------------------------
  245. Id:        KV-7
  246.  
  247. Location:  Compiler Optimization
  248.  
  249. Version:  Exists in BC 3.0. I haven't tested 3.1
  250.  
  251.  
  252. Problem:   The -Ob (dead code) optimization and multiple floating
  253.            point assignments causes valid store to be eliminated.
  254.  
  255.            Example:
  256.                a = b = 3.0;
  257.  
  258.                If a was not used, the store to b would also be
  259.                eliminated
  260.  
  261.            Relayed in a message from Jeff Stock, originally posted by
  262.            Mel Corey.
  263.  
  264. Proposed Solution:
  265.            Disable -Ob for dead assignments, eliminate unused variables,
  266.            or store unused variables separately.
  267.  
  268. ------------------------------------------------------------------
  269. Id:        KV-8
  270.  
  271. Location:  Compiler
  272.  
  273. Version:  Exists in BC 3.0. I haven't tested 3.1
  274.  
  275. Problem:   A for loop with a constant test expression that evaluates
  276.            to 0 has its body executed once.  (I'm not sure if this is
  277.            related to any optimizations).
  278.  
  279.            Example:
  280.  
  281.                 for( ;0; ) { /*this executes once*/ }
  282.  
  283.            I took this from a message by Mark Sidell.
  284.  
  285. Proposed Solution:
  286.           Eliminate the loop, or use a variable in the condition.
  287.  
  288.  
  289. ------------------------------------------------------------------
  290. Id:        KV-9
  291.  
  292. Location:  C++ Compiler
  293.  
  294. Version:  Exists in BC 3.0. Fixed in 3.1
  295.  
  296. Problem:  Temporary destructor is not called in certain situations.  Thanks
  297.           to a forum message by Mark Sidell for this example (confirmed by
  298.           Eric Nagler).
  299.  
  300.     The following program demonstrates what appears to be a bug in BCC 3.0.
  301.     The Der ctor creates a temporary Item and passes its address
  302.     to the base ctor.  But, the temporary Item is never destroyed!
  303.  
  304.     #include <iostream.h>
  305.  
  306.     struct Item {
  307.       Item() { cout << "Item ctor\n"; }
  308.       ~Item() { cout << "Item dtor\n"; }
  309.     };
  310.  
  311.     struct Base {
  312.       Base( Item *) {}
  313.     };
  314.  
  315.     struct Der : Base {
  316.       // Yikes!  The temporary Item is never destroyed.
  317.       Der() : Base( &Item()) {}
  318.     };
  319.  
  320.     int main()
  321.     {
  322.       Der oDer;
  323.       return 0;
  324.     }
  325.  
  326. ------------------------------------------------------------------
  327. Id:        KV-10
  328.  
  329. Location:  VROOMing TurboVision programs (and potentially others)
  330.  
  331. Version:  Exists in BC 3.0 and in 3.1
  332.  
  333. Problem: The linker warning:
  334.               No stub for fixup at _DATA:xxxx in module yyyy.CPP
  335.  
  336.          Is reported for certain VROOMed modules.  These modules do
  337.          not execute correctly.
  338.  
  339. Proposed Solution:
  340.          The problem seems to be related to incorrect code produced
  341.          for certain virtual tables.  Only modules which inherit certain
  342.          classes or constructors develop the problem.
  343.          The solution (taken from the TurboVision make file) is to compile
  344.          these modules with the -B and -Vs options (compile via assembly
  345.          and local virtual tables).  Note that this requires more memory,
  346.          and may not be possible in the IDE, thus forcing you to a make
  347.          file.  Personally, I don't VROOM these modules until I'm ready
  348.          for a release, when I do it with a make file.
  349.  
  350.          However, if you have enough modules, you will notice that you
  351.          still get the 'No stub' error even though you compiled it -Vs -B.
  352.          First, try it *without* the -Vs -B (still overlaid).  Oddly enough,
  353.          some modules work this way.
  354.  
  355.          Unfortunately, some modules don't work in either compilation
  356.          option.  Your only option (as far as I know) is to leave those
  357.          ones non-overlaid.  If you find a way to overlay all modules,
  358.          **please send me mail**.  It would really be a big help to me.
  359.  
  360. ------------------------------------------------------------------
  361. Id:        KV-11
  362.  
  363. Location:  Compiler
  364.  
  365. Version:  Exists in BC 3.0.  Fixed in 3.1
  366.  
  367. Problem:  The compiler generates bad code for 1, 2 and 4 byte array
  368.           initializers.  Thanks to TechMate, Inc. for the following
  369.           simple example (I took it from one of their forum msgs).  Jeff
  370.           Stock of Borland confirmed that it was a problem.
  371.  
  372.           #pragma inline
  373.           void main()
  374.           {
  375.             char x[4] = "bug";
  376.           };
  377.  
  378.         When compiled with the following command line:
  379.           bcc -ml x.cpp
  380.  
  381.         It generates assembly code with this error:
  382.  
  383.           Assembling file:   x.ASM
  384.           **Error** x.ASM(45) Too few operands to instruction
  385.           Error messages:    1
  386.           Warning messages:  None
  387.           Passes:            1
  388.           Remaining memory:  378k
  389.  
  390.  
  391. Proposed Solution:
  392.          Don't use 1, 2 or 4 byte arrays (use other sizes)?
  393. ------------------------------------------------------------------
  394. Id:        KV-12
  395.  
  396. Location:  Compiler
  397.  
  398. Version:  Exists in BC 3.0.  Fixed in 3.1
  399.  
  400. Problem:   Certain shifting operations do not perform correctly with
  401.            jump optimization.
  402.  
  403.            Example:
  404.  
  405.                 #include <stdio.h>
  406.  
  407.                 long Fct (void)
  408.                 {
  409.                   int a, b;
  410.                   a = 1;
  411.                   b = 0;
  412.                   // try this with and without jump optimization
  413.                   return (((long)a) | (((long)b)<<16));
  414.                 }
  415.  
  416.                 main ()
  417.                 {
  418.                   long l, Ref;
  419.                   Ref = Fct ();
  420.                   l = ((long)1)<<16;
  421.                   printf ("Long: %lX\n", l);
  422.                   printf ("Return: %lX\n", Ref);
  423.                   return 0;
  424.                 }
  425.  
  426.  
  427.            This was confirmed by Jeff Stock at Borland.
  428.  
  429. Proposed Solution:
  430.          Turn off jump optimization for those modules that use shifting
  431.          similar to that shown above.
  432.  
  433. ------------------------------------------------------------------
  434. Id:        KV-13
  435.  
  436. Location:  TV
  437.  
  438. Version:  Exists in BC 3.0 and in 3.1 (although TOBJSTRM has changed
  439.           in 3.1, so my original line numbers don't quite work).
  440.  
  441. Problem:   The TurboVision stream manager (contained in TOBJSTRM)
  442.            is very unstable if the file you read is corrupted for any
  443.            reason.  There are several calls to assert(), which does
  444.            not uninstall the mouse interrupt when exiting, as well as
  445.            some possible null pointer writing if there are problems.
  446.  
  447.            Also, If you try to read an object whose class is not registered
  448.            with the stream manager (or a corrupt file), this crashes
  449.            your application.
  450.  
  451. Proposed Solution:
  452.            I have modified TOBJSTRM to set the stream error flag when
  453.            an error occurs.  The first step is to replace
  454.            all occurances of assert() with the following two macros:
  455.  
  456. ** START **
  457. #define fassert(Condition) if (!(Condition)) \
  458.                    { setstate(ios::badbit); errno = EINVDAT; }
  459. #define foassert(Condition,Obj) if (!(Condition)) \
  460.              { (Obj).setstate(ios::badbit); errno = EINVDAT; }
  461. ** END **
  462.  
  463. Next, you should add some additional tests.  In particular, the
  464. two calls to ipstream::readPrefix, which returns a pointer, do
  465. not check if this pointer is NULL (object not found).  Change
  466. these lines to read something like:
  467.  
  468. **** FROM ****
  469. const TStreamableClass *pc = ps.readPrefix();
  470. ps.readData( pc, &t );
  471. ps.readSuffix();
  472. ** END **
  473.  
  474. ***** TO ******
  475. const TStreamableClass *pc = ps.readPrefix();
  476. foassert (pc != 0, ps);
  477. if (pc != 0)
  478. {
  479.   ps.readData( pc, &t );
  480.   ps.readSuffix();
  481. }
  482. ** END **
  483.  
  484. Also, add the following to the start of BOTH versions of
  485. ipstream::readString:
  486. ** START **
  487. if (!good())
  488.   return 0;
  489. ** END **
  490.  
  491. Add the following to the start of ipstream& operator >>
  492. for TStreamable&:
  493. ** START **
  494. if (!ps.good())
  495.   return ps;
  496. ** END **
  497.  
  498. Add the following to the start of ipstream& operator >> for
  499. void *&:
  500. ** START **
  501. if (!ps.good())
  502. {
  503.   t = 0;
  504.   return ps;
  505. }
  506. ** END **
  507.  
  508. I'm sure there are more areas, but these have served me well.
  509.  
  510. ------------------------------------------------------------------
  511. Id:        KV-14
  512.  
  513. Location:  TV
  514.  
  515. Version:  Exists in BC 3.0.  Appears fixed 3.1
  516.  
  517. Problem:   Your application will crash with an abnormal program termination
  518.            if you try to do a file dialog in a directory with too many
  519.            files.
  520.  
  521. Proposed Solution:
  522.  
  523. Interestingly enough, TFILLIST has code to handle too many
  524. files.  The loop to read a file diligently checks if the
  525. pointer (allocated via new) is NULL, and will terminate the
  526. loop.  However, NEW.CPP provided with TurboVision will
  527. *terminate* when no more memory is available (after dipping
  528. into the safety pool).  TFILLIST should check the Safety Pool
  529. (via lowMemory) while it is looping, NOT the pointer itself.
  530.  
  531. In TFileList::readDirectory, change lines 167 and 179 as follows:
  532. **** FROM *****
  533. while( p != 0 && res == 0)
  534. ***** TO *****
  535. while( p != 0 && res == 0 && !lowMemory())
  536. ***** END *****
  537.  
  538. Also, change line 214:
  539. **** FROM *****
  540. if( p == 0 )
  541.     messageBox( tooManyFiles, mfOKButton | mfWarning );
  542. ***** TO *****
  543. if( p == 0  || lowMemory())
  544.     messageBox( tooManyFiles, mfOKButton | mfWarning );
  545. ***** END *****
  546. ------------------------------------------------------------------
  547. Id:        KV-15
  548.  
  549. Location:  TV
  550.  
  551. Version:  Exists in BC 3.0. Appears to still exist in 3.1 (although
  552.           they fixed some other problems there)
  553.  
  554. Problem:
  555.  A descendent of TNSCollection which sets the shouldDelete
  556.  member to false will leave one unfreed pointer if it is
  557.  destroyed when items are contained within it.
  558.  
  559.  The TOBJSTRM module creates just such a collection during
  560.  a read of a stream containing TStreamable *.
  561.  
  562. Proposed Solution:
  563.  The problem appears to be as follows.  The shutDown function
  564.  of TNSCollection calls the member function setLimit(0) to
  565.  free all object pointers.  setLimit *never* lets limit fall
  566.  below count.  Thus, if you destroy a TNSCollection with
  567.  count > 0, the items member of TNSCollection will never be
  568.  freed. (Note that I'm not talking about the contained pointers,
  569.  which are obviously the responsibility of the caller in a
  570.  shouldDelete = False situation).
  571.  
  572.  To fix the problem, change line 57 of TCOLLECT.CPP:
  573. **** FROM *****
  574. void TNSCollection::shutDown()
  575. {
  576.     if( shouldDelete )
  577.         freeAll();
  578.     setLimit(0);
  579.     TObject::shutDown();
  580. }
  581. ***** TO *****
  582. void TNSCollection::shutDown()
  583. {
  584.     if( shouldDelete )
  585.         freeAll();
  586.     else
  587.         count = 0;
  588.     setLimit(0);
  589.     TObject::shutDown();
  590. }
  591. ***** END *****
  592.  
  593. ------------------------------------------------------------------
  594. Id:        KV-16
  595.  
  596. Location:  TV
  597.  
  598. Version:  Exists in BC 3.0. Appears to still exist in 3.1 (although
  599.           they fixed some other problems there)
  600.  
  601. Problem:
  602.   Typing too many characters into the input line of a file dialog
  603.   will crash the program.
  604.  
  605. Proposed Solution:
  606.   The problem arises when you call getFileName with a string of
  607.   size MAXPATH.  TFILDLG has a line that can cause the
  608.   resultant path to be approximately 2 * MAXPATH.
  609.  
  610.   To avoid the problem, you can either pass a string of
  611.   size MAXPATH * 2 into getFileName, OR make the following 2 changes
  612.   to TFILDLG:
  613.  
  614. **** FROM (Circa line 178) *****
  615. static void trim( char *dest, const char *src )
  616. {
  617.     while( *src != EOS && isspace( *src ) )
  618.         src++;
  619.     while( *src != EOS && !isspace( *src ) )
  620.         *dest++ = *src++;
  621.     *dest = EOS;
  622. }
  623. ***** TO *****
  624. static void trim( char *dest, const char *src, int size )
  625. {
  626.     while( *src != EOS && isspace( *src ) )
  627.         src++;
  628.     while( *src != EOS && !isspace( *src ) && --size > 0)
  629.         *dest++ = *src++;
  630.     *dest = EOS;
  631. }
  632.  
  633. **** FROM (Circa line 196) *****
  634.     trim( s, fileName->data );
  635.     if( relativePath( s ) == True )
  636.         {
  637.         strcpy( s, directory );
  638.         trim( s + strlen(s), fileName->data );
  639.         }
  640. ***** TO *****
  641.     trim( s, fileName->data, MAXPATH - 1 );
  642.     if( relativePath( s ) == True )
  643.         {
  644.         strcpy( s, directory );
  645.         trim( s + strlen(s), fileName->data, MAXPATH - 1 - strlen (s) );
  646.         }
  647. ***** END ****
  648.  
  649. ------------------------------------------------------------------
  650. Id:        KV-17
  651.  
  652. Location:  TV (with VROOM)
  653.  
  654. Version:  Exists in BC 3.0. Appears to still exist in 3.1
  655.  
  656. Problem:
  657.   A VROOM'ed TurboVision application has a very subtle problem which can
  658.   cause very rare program crashes.
  659.  
  660. Proposed Solution:
  661.   The problem occurs TEVENT.CPP is compiled with overlays off and
  662.   standard stack frame off.  TEventQueue::suspend (a very short
  663.   function) compiles into single call to THWMouse::suspend (a VROOMed
  664.   module, by default) without the standard stack frame.
  665.  
  666.   If the overlay manager is invoked when THWMouse is called, it may
  667.   lose the correct return address to the caller of TEventQueue::suspend
  668.   (TProgram::suspend).  This causes a program crash.
  669.  
  670.   The solution is to compile TEVENT.CPP with standard stack frame (-k)
  671.   Change MAKEFILE in the TVISION\SOURCE directory as follows:
  672.  
  673. **** FROM (Line 218) *****
  674. tevent.obj : tevent.cpp
  675.      $(BCC) -Y- $&.cpp
  676. ***** TO *****
  677. tevent.obj : tevent.cpp
  678.      $(BCC) -Y- -k $&.cpp
  679. ***** END ****
  680.  
  681. ------------------------------------------------------------------
  682. Id:        KV-18
  683.  
  684. Location:  TV (with VROOM)
  685.  
  686. Version:  Exists in BC 3.0. Appears to still exist in 3.1
  687.  
  688. Problem:
  689.   A VROOM'ed TurboVision application crashes when you get a critical error.
  690.  
  691. Proposed Solution:
  692.   The basic problem is that you must make sure that the overlay manager
  693.   CANNOT be called in a critical error handler (it uses those hi-numbered
  694.   DOS function calls).  The powers that be at Borland correctly remove
  695.   SYSERR.CPP and SYSINT.CPP from the overlay list.  However, SYSERR.CPP
  696.   calls the following modules, which must also be removed from the list:
  697.  
  698.        tscreen
  699.        drivers
  700.        swapst
  701.        tevent
  702.  
  703.   The solution is to compile these modules separately in the TV makefile,
  704.   and include them NON-overlayed in your program.  I create a library with
  705.   the six non-overlayed files.  Creating the library also avoids BC 3.0
  706.   nasty habit of relinking every time you run because it can't find the
  707.   source for the .OBJ files.
  708.  
  709. ------------------------------------------------------------------
  710. Id:        KV-19
  711.  
  712. Location:  TV
  713.  
  714. Version:  Exists in BC 3.0. Appears to still exist in 3.1
  715.  
  716. Problem:
  717.    The critical error handler crashes if it receives errors which are
  718.    greater than 14 (i.e., it happens on a network, etc.).
  719.  
  720. Proposed Solution:
  721.   The problem lies in SYSERR.CPP, which blindly assumes that all errors
  722.   are less than 14 when it indexes its error name array.  Simply put
  723.   a check for the index in the code.
  724.  
  725.  To fix the problem, change line 122 of TCOLLECT.CPP:
  726. **** FROM *****
  727.     sprintf( s, errorString[ errorCode ], drive + 'a' );
  728. ***** TO *****
  729.     if (errorCode < sizeof (errorString) / sizeof (*errorString))
  730.         sprintf( s, errorString[ errorCode ], drive + 'a' );
  731.     else
  732.         sprintf( s, "Critical error %d", errorCode);
  733.  
  734. ***** END *****
  735.